package metier;

import java.awt.Color;
import java.util.*;

import org.omg.CORBA.Object;

import utiles.FourmizLogger;

/**
 * Classe représentant une fourmi.<br />
 * Une fourmi peut se trouver dans 5 états différents représentés par un int : -
 * 1 : c'est une éclaireuse - 2 : c'est une fourmi qui doit impérativement
 * retrouver la fourmillière - 3 : c'est une fourmi qui suit une piste de
 * phéromones - 4 : c'est une fourmi qui est mort naturellement - 5 : c'est une
 * fourmi qui est morte de faim (la pauvre)
 *
 * @author Thomas Folgueral
 * @author Julien Pernot
 * @author Guillaume Peyet
 * @author Guillaume Sainthillier
 */
public class Fourmi extends Element {

    private int dureeVie;
    private int quantiteNourritureTransportable;
    private IDeplacable deplacement;
    protected Fourmiliere f;
    private ArrayList<Position> positions;
    private Etat etat;
    private int quantiteNourritureRecoltee;
    private Position ancetrePos;
    
    private enum Etat {
        ECLAIREUSE,
        RETOUR,
        SUIVEUSE,
        MORT_NATURELLE,
        MORT_FAMINE
    };

    public Fourmi(int dureeVie, int quantiteNourritureTransportable, Fourmiliere f) {
        this(10, 5, f, dureeVie, quantiteNourritureTransportable);
    }

    public Fourmi(int longueur, int largeur, Fourmiliere f, int dureeVie, int quantiteNourritureTransportable) {
        super(longueur, largeur);

        this.couleur = Color.RED;
        this.f = f;
        this.deplacement = new Astar();
        this.dureeVie = dureeVie;
        this.quantiteNourritureTransportable = quantiteNourritureTransportable;
        this.etat = Etat.ECLAIREUSE;
        this.quantiteNourritureRecoltee = 0;
        this.ancetrePos = null;
        f.addFourmi(this);
    }

    /**
     * Une fourmi qui agit peut: Chercher de la nourriture Retourner à la
     * fourmilière lorsqu'elle a trouvé de la nourriture
     *
     * @param c la carte sur laquelle agir
     */
    @Override
    public void agir(Carte c) {
        this.dureeVie--;

        // on verifie si la fourmi est toujours vivante
        if (this.dureeVie <= 0) {
            this.etat = Etat.MORT_NATURELLE;
        }

        // on recupere la position courrante de la fourmi
        Position current = c.getPosition(this);

        // on verifie s'il n'y a pas de nourriture dans la case courrante
        ArrayList<Element> elements = c.getElements(current);

        boolean nourriture = false;
        boolean concentration = false;
        for (Element elem : elements) {
            if (elem instanceof Nouriturre && ((Nouriturre) elem).getQuantiteNourritureDisponible() != 0) {
                nourriture = true;
            }
            concentration = concentration | (elem instanceof Pheromone);
        }

        switch (this.etat) {
            case ECLAIREUSE:
                if (nourriture) {
                    // recolter la nourriture
					this.recolteNourriture(c);
					this.devenirRetour(c);
                } else if (concentration) {
					// La fourmi devient une suiveuse
					this.devenirSuiveuse(c);
                } else {
					// La fourmi cherche aléatoirement autour d'elle pour trouver de la nourriture
					this.deplacementAleatoire(c, current);
                }
                break;
            case RETOUR:
				// La fourmi se dirige vers la fourmilière
				this.deplacementPathFinding(c, current);
                break;
            case SUIVEUSE:
                // Suivi de pheromones
                if (nourriture) {
                    // De la nourriture a été trouvée, on la recolte
					this.recolteNourriture(c);
					this.devenirRetour(c);
                } else {
					// Sinon on suit la trace des pheromones
					this.suiviPheromone(c);
                }
                break;
            case MORT_NATURELLE:
            case MORT_FAMINE:
				// La fourmi meurt
				this.mourir(c);
                break;
        }
    }

    /**
     * Fait bouger une fourmi sur la carte
     *
     * @param c la carte sur laquelle se déplacer
     * @param p la position souhaitée
     */
    public void bouger(Carte c, Position p) {
        Position current = c.getPosition(this);
        FourmizLogger.debug(this, "Une fourmis bouge de " + current + " vers " + p);
        this.setChanged();
        this.notifyObservers(p);
        this.ancetrePos = current;
    }
	
	private void devenirRetour(Carte c) {
		this.etat = Etat.RETOUR;
		this.couleur = Color.BLUE;
		
		this.agir(c);
	}

	private void devenirSuiveuse(Carte c) {
		this.couleur = Color.magenta;
        this.etat = Etat.SUIVEUSE;
		
		this.agir(c);
	}
	
	private void devenirEclaireuse(Carte c) {
		this.etat = Etat.ECLAIREUSE;
		this.couleur = Color.RED;
	}

	private void recolteNourriture(Carte c) {
		FourmizLogger.info(this, "Nourriture trouvée");
		this.quantiteNourritureRecoltee = c.recolteNourriture(this, this.quantiteNourritureTransportable);
	}

	private void deplacementAleatoire(Carte c, Position positionDepart) {
		// on determine la prochaine position aleatoirement
		Random rand = new Random();
		Position nextPosition = null;
		while (nextPosition == null) {
			int mvVertical = rand.nextInt(3) - 1;
			int mvHorizontal = rand.nextInt(3) - 1;

			if (!(mvVertical == 0 && mvHorizontal == 0)) {
				int destX = positionDepart.getX();
				int destY = positionDepart.getY();

				destX += mvVertical;
				destY += mvHorizontal;

				if (destX >= 0 && destX < c.getLargeur()
						&& destY >= 0 && destY < c.getLongueur()) {
					// On vérifie que la case n'est pas infranchissable
					nextPosition = new Position(destX, destY);
					for (Element elem : c.getElements(nextPosition)) {
						if (elem instanceof Obstacle) {
							Obstacle obstacle = (Obstacle) elem;
							int difficulte = obstacle.getDifficulte();
							if (difficulte == 2000) {
								nextPosition = null;
							}
						}
					}

				}
			}

			if (nextPosition != null && this.ancetrePos != null) {
				if (nextPosition.toString().equals(this.ancetrePos.toString())) {
					nextPosition = null;
				}
			}
		}

		// on demande e la carte de lui affecter la nouvelle position
		this.bouger(c, nextPosition);
	}

	private void deplacementPathFinding(Carte c, Position possitionCourante) {
		if (this.positions == null || this.positions.isEmpty()) {
			// on determine le chemin pour rentrer e la fourmilliere
			this.positions = this.deplacement.pathfinding(c, possitionCourante, c.getPosition(this.f));
		}

		if (!this.positions.isEmpty()) {
			// On dépose un phéromone
			c.deposerPheromone(this, this.ancetrePos);

			// On se déplace
			Position go_to = this.positions.remove(0);
			this.bouger(c, go_to);
		} else {
			// on depose la nourriture dans la fourmilliere
			FourmizLogger.info(this, "Nourriture déposée");
			this.f.deposerNourriture(this.quantiteNourritureRecoltee);
			this.quantiteNourritureRecoltee = 0;
			
			this.devenirEclaireuse(c);
		}
	}

	private void suiviPheromone(Carte c) {
		// On récupère la pheromone qui se trouve sur la case courrante
		Position position = c.getPosition(this);

		Pheromone pheromoneCourante = null;
		for (Element element : c.getElements(position)) {
			if (element instanceof Pheromone) {
				pheromoneCourante = (Pheromone) element;
			}
		}

		if (pheromoneCourante == null) {
			this.devenirEclaireuse(c);
		} else {
			// On se déplace en fonction de la direction de la pheromone
			Position nextPosition = pheromoneCourante.getDirection();
			if (nextPosition != null) {
				this.bouger(c, nextPosition);
			}
		}
	}

	private void mourir(Carte c) {
		FourmizLogger.info(this, "Mort d'une fourmi");
		c.removeElement(this);
		this.f.removeFourmi(this);
	}
	
	void meurtFaim() {
        this.etat = Etat.MORT_FAMINE;
	}

    /*
     Getters and Setter
     */
    public int getDureeVie() {
        return dureeVie;
    }

    public void setDureeVie(int dureeVie) {
        this.dureeVie = dureeVie;
    }

    public int getQuantiteNourritureTransportable() {
        return quantiteNourritureTransportable;
    }

    public void setQuantiteNourritureTransportable(int quantiteNourritureTransportable) {
        this.quantiteNourritureTransportable = quantiteNourritureTransportable;
    }

    public Position getAncetrePos() {
        return ancetrePos;
    }

    @Override
    public String toString() {
        return "f";
    }
}
